package tms.unitTests;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;


import static org.junit.Assert.*;


import org.junit.Before;
import org.junit.Test;

import tms.model.*;

/**
 * TaskUnitTests class contains the units tests of the methods in the Task class 
 * 
 * @author Rolf Schmidt
 *
 */
public class TaskUnitTests {
	/// a header line used in converting a person or a task to a string
	final String H_LINE = "---------------------------------------";
	/// format of the date
	final String DATE_FORMAT = "MM/dd/yyyy";
	/// a long task duration
	final int LONG_DURATION = 1548765412;
	/// number of characters in a long string
	final int LONG_STRING = 100;
	/// number of characters in a very long string
	final int VERY_LONG_STRING = 2000;
	/// a string containing wild characters
	final String WILD_STRING = "*@&(|}{+?~+\\/";
	/// stringbuffer for long string
	StringBuffer stringBufferLong;
	/// stringbuffer for a very long string
	StringBuffer stringBufferVeryLong;
	
	/**
	 * default constructor
	 */
	public TaskUnitTests() {
	}

	/**
	 * helper method called before every unit test to create a long and a very long string buffers
	 */
	@Before
	public void setUp() {
		stringBufferLong = new StringBuffer(LONG_STRING);
		for (int i = 0; i < LONG_STRING; i++) {
			stringBufferLong.append("a");
		}
		
		stringBufferVeryLong = new StringBuffer(VERY_LONG_STRING);
		for (int i = 0; i < VERY_LONG_STRING; i++) {
			stringBufferVeryLong.append("a");
		}
    }
	
	/**
	 * test the default values assigned to a task object in the task default constructor
	 */
	@Test
	public void testTaskConstructor() {
		Task task = new Task();
		
		assertEquals(0, task.getId());
		assertEquals("none", task.getTitle());
		assertEquals("none", task.getDescription());
		assertEquals(1, task.getDuration());
		assertEquals("none", task.getDeliverable());
		assertEquals("nobody", task.getPerson());
	}

	/**
	 * test converting a newly created task object to a string
	 * the default constructor in task class will be used to create the task object
	 */
	@Test
	public void testTaskToString1() {
		Task task = new Task();
		StringBuffer sb = new StringBuffer(H_LINE);
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		
		sb.append("\nID: 0");
		sb.append("\nTitle: none");
		sb.append("\nDescription: none");
		sb.append("\nDuration: 1");
		sb.append("\nDeliverable: none");
		sb.append("\nDeadline: " + testDeadline);
		sb.append("\nPerson: nobody");
		sb.append("\n" + H_LINE + "\n");

		assertEquals(sb.toString(), task.toString());
	}

	/**
	 * test converting a task object to a string
	 * the task attributes are not the same as the default ones
	 */
	@Test
	public void testTaskToString2() {
		
		// create a task
		Task task = new Task();
		StringBuffer sb = new StringBuffer(H_LINE);
		Date testDeadline = Calendar.getInstance().getTime();
		// modify the task
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson("John Doe");
		task.setDeadline(testDeadline);

		// construct the expected value
		sb.append("\nID: 1");
		sb.append("\nTitle: Test title");
		sb.append("\nDescription: Test description");
		sb.append("\nDuration: 25");
		sb.append("\nDeliverable: Test deliverable");
		sb.append("\nDeadline: " + testDeadline);
		sb.append("\nPerson: John Doe");
		sb.append("\n" + H_LINE + "\n");

		// test
		assertEquals(sb.toString(), task.toString());
	}

	/**
	 * test that a task object is equal to itself
	 */
	@Test
	public void testTaskEquals1() {
		Task task = new Task();
		assertTrue(task.equals(task));
	}
	
	/**
	 * test that two different task objects are equal when all of their attributes are the same
	 * the ids and deadlines are set in the test and the rest of attributes are defaults
	 */
	@Test
	public void testTaskEquals2() {
		
		// create two tasks
		Task task1 = new Task();
		Task task2 = new Task();
		Date testDeadline = Calendar.getInstance().getTime();

		// set the same id and deadline to the tasks
		task1.setId(1);
		task1.setDeadline(testDeadline);
		task2.setId(1);
		task2.setDeadline(testDeadline);

		// test the equality
		assertTrue(task1.equals(task2));
	}
	
	/**
	 * test that two task objects are different if their ids are different
	 */
	@Test
	public void testTaskEquals3() {
		// create two tasks
		Task task1 = new Task();
		Task task2 = new Task();

		// set different ids to the tasks
		task1.setId(1);
		task2.setId(2);

		// test the inequality
		assertFalse(task1.equals(task2));
	}
	
	/**
	 * test that a task object is not equal to an object of different type
	 */
	@Test
	public void testTaskEquals4() {
		Task task = new Task();

		String test = "test different object";

		// test that task is not equal to string
		assertFalse(task.equals(test));
	}
	
	/**
	 * test that a task object is not equal to null 
	 */
	@Test
	public void testTaskEquals5() {
		Task task = new Task();
		assertFalse(task.equals(null));
	}
	
	/**
	 * test that two task objects are different if any of their attributes are different
	 * (id, title, description, deliverable, deadline, person name) 
	 */
	@Test
	public void testTaskEquals6() {
		Task task1 = new Task();
		Task task2 = new Task();
		Date testDeadline = Calendar.getInstance().getTime();

		task1.setDeadline(testDeadline);
		task2.setDeadline(testDeadline);

		// set different ids
		task1.setId(1);
		task2.setId(5);
		assertFalse(task1.equals(task2));
		// make them equal
		task2.setId(1); 
		
		// set different titles
		task1.setTitle("hi");
		task2.setTitle("bye");
		assertFalse(task1.equals(task2));
		// make them equal
		task2.setTitle("hi");
		
		// set different Descriptions
		task1.setDescription("des1");
		task2.setDescription("desc2");
		assertFalse(task1.equals(task2));
		// make them equal
		task2.setDescription("none");
		
		// set different deliverables
		task1.setDeliverable("del1");
		task2.setDeliverable("del2");
		assertFalse(task1.equals(task2));
		// make them equal
		task2.setDeliverable("none");
		
		// set different deadlines
		task2.setDeadline(null);
		assertFalse(task1.equals(task2));
		// make them equal
		task2.setDeadline(testDeadline);
		
		// set different person name
		task1.setPerson("sam");
		task2.setPerson(null);
		assertFalse(task1.equals(task2));
	}
	
	/*
	 * test setting id of the task to a positive number
	 * expect successful operation
	 */
	@Test
	public void testTaskSetId1() {
		Task task = new Task();
		
		task.setId(1);
		assertEquals(1, task.getId());
	}
	
	/*
	 * test setting id of the task to zero
	 * expect successful operation
	 */
	@Test
	public void testTaskSetId2() {
		Task task = new Task();
		
		task.setId(0);
		assertEquals(0, task.getId());
	}
	
	/*
	 * test setting id of the task to an invalid number (negative)
	 * expect successful operation; however, the id is set to a default value (zero)
	 */
	@Test
	public void testTaskSetId3() {
		Task task = new Task();

		task.setId(-1);
		assertEquals(0, task.getId());
	}
	
	/*
	 * test setting title of the task to an empty string
	 * expect successful operation; however, the title is set to a default value (none)
	 */
	@Test
	public void testTaskSetTitle1() {
		Task task = new Task();
		
		task.setTitle("");
		assertEquals("none", task.getTitle());
	}
	
	/*
	 * test setting title of the task to a long string
	 * expect successful operation
	 */
	@Test
	public void testTaskSetTitle2() {
		Task task = new Task();

		task.setTitle(stringBufferLong.toString());
		assertEquals(stringBufferLong.toString(), task.getTitle());
	}
	
	/*
	 * test setting title of the task to null
	 * expect successful operation; however, the title is set to a default value (none)
	 */
	@Test
	public void testTaskSetTitle3() {
		Task task = new Task();

		task.setTitle(null);
		assertEquals("none", task.getTitle());
	}
	
	/*
	 * test setting title of the task to a string containing wild characters
	 * expect successful operation
	 */
	@Test
	public void testTaskSetTitle4() {
		Task task = new Task();
		
		task.setTitle(WILD_STRING);
		assertEquals(WILD_STRING, task.getTitle());
	}
	
	/*
	 * test setting description of the task to an empty string
	 * expect successful operation; however, the description is set to a default value (none)
	 */
	@Test
	public void testTaskSetDescription1() {
		Task task = new Task();
		
		task.setDescription("");
		assertEquals("none", task.getDescription());
	}
	
	/*
	 * test setting description of the task to a very long string
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDescription2() {
		Task task = new Task();
		
		task.setDescription(stringBufferVeryLong.toString());
		assertEquals(stringBufferVeryLong.toString(), task.getDescription());
	}
	
	/*
	 * test setting description of the task to null
	 * expect successful operation; however, the description is set to a default value (none)
	 */
	@Test
	public void testTaskSetDescription3() {
		Task task = new Task();

		task.setDescription(null);
		assertEquals("none", task.getDescription());
	}
	
	/*
	 * test setting description of the task to a string containing wild characters
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDescription4() {
		Task task = new Task();
		
		task.setDescription(WILD_STRING);
		assertEquals(WILD_STRING, task.getDescription());
	}
	
	/*
	 * test setting duration of the task to a valid number (positive number greater than zero)
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDuration1() {
		Task task = new Task();
		
		task.setDuration(10);
		assertTrue(task.getDuration() == 10);
	}
	
	/*
	 * test setting duration of the task to an invalid number (negative number)
	 * expect successful operation; however, the duration is set to a default value (1)
	 */
	@Test
	public void testTaskSetDuration2() {
		Task task = new Task();
		
		task.setDuration(-10);
		assertTrue(task.getDuration() == 1);
	}
	
	/*
	 * test setting duration of the task to an invalid number (zero)
	 * expect successful operation; however, the duration is set to a default value (1)
	 */
	@Test
	public void testTaskSetDuration3() {
		Task task = new Task();
		
		task.setDuration(0);
		assertTrue(task.getDuration() == 1);
	}
	
	/*
	 * test setting duration of the task to a valid number but very long duration
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDuration4() {
		Task task = new Task();
		
		task.setDuration(LONG_DURATION);
		assertTrue(task.getDuration() == LONG_DURATION);
	}
	
	/*
	 * test setting duration of the task to a valid number but MAX integer value
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDuration5() {
		Task task = new Task();
		
		task.setDuration(Integer.MAX_VALUE);
		assertTrue(task.getDuration() == Integer.MAX_VALUE);
	}
	
	/*
	 * test setting deliverable of the task to an empty string
	 * expect successful operation; however, the deliverable is set to a default value (none)
	 */
	@Test
	public void testTaskSetDeliverable1() {
		Task task = new Task();
		
		task.setDeliverable("");
		assertEquals("none", task.getDeliverable());
	}
	
	/*
	 * test setting deliverable of the task to along string
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDeliverable2() {
		Task task = new Task();

		task.setDeliverable(stringBufferLong.toString());
		assertEquals(stringBufferLong.toString(), task.getDeliverable());
	}
	
	/*
	 * test setting deliverable of the task to null
	 * expect successful operation; however, the deliverable is set to a default value (none)
	 */
	@Test
	public void testTaskSetDeliverable3() {
		Task task = new Task();

		task.setDeliverable(null);
		assertEquals("none", task.getDeliverable());
	}
	
	/*
	 * test setting deliverable of the task to a string containing wild characters
	 * expect successful operation
	 */
	@Test
	public void testTaskSetDeliverable4() {
		Task task = new Task();
		
		task.setDescription(WILD_STRING);
		assertEquals(WILD_STRING, task.getDescription());
	}
	
	/*
	 * test setting deadline of the task to a valid date (present)
	 * expect successful operation
	 */
	@SuppressWarnings("deprecation")
	@Test
	public void testTaskSetDeadline1() {
		Task task = new Task();
		Date now = Calendar.getInstance().getTime();
		Date testDeadline = Calendar.getInstance().getTime();

		task.setDeadline(now);
		assertTrue(task.getDeadline() instanceof Date);
		
		testDeadline.setMonth(testDeadline.getMonth() + 1);
		task.setDeadline(testDeadline);
		assertTrue(task.getDeadline() instanceof Date);
	}

	/*
	 * test setting deadline of the task to a valid date (past)
	 * expect successful operation
	 */
	@SuppressWarnings("deprecation")
	@Test
	public void testTaskSetDeadline2() {
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();

		testDeadline.setMonth(testDeadline.getMonth() - 1);
		task.setDeadline(testDeadline);
		assertTrue(task.getDeadline() instanceof Date);
	}
	
	/*
	 * test setting deadline of the task to null
	 * expect successful operation; however the deadline is set to a default value (now)
	 */
	@Test
	public void testTaskSetDeadline3() {
		Task task = new Task();
		
		task.setDeadline(null);
		assertTrue(task.getDeadline() instanceof Date);
	}

	/*
	 * test setting person name (assignee) of the task to an empty string
	 * expect successful operation; however the assignee is set to a default value (nobody)
	 */
	@Test
	public void testTaskSetPerson1() {
		Task task = new Task();
		
		task.setPerson("");
		assertEquals("nobody", task.getPerson());
	}
	
	/*
	 * test setting person name (assignee) of the task to a long string
	 * expect successful operation
	 */
	@Test
	public void testTaskSetPerson2() {
		Task task = new Task();

		task.setPerson(stringBufferLong.toString());
		assertEquals(stringBufferLong.toString(), task.getPerson());
	}
	
	/*
	 * test setting person name (assignee) of the task to null
	 * expect successful operation; however the assignee is set to a default value (nobody)
	 */
	@Test
	public void testTaskSetPerson3() {
		Task task = new Task();

		task.setPerson(null);
		assertEquals("nobody", task.getPerson());
	}
	
	/*
	 * test setting person name (assignee) of the task to a string containing wild characters
	 * expect successful operation
	 */
	@Test
	public void testTaskSetPerson4() {
		Task task = new Task();
		
		task.setPerson(WILD_STRING);
		assertEquals(WILD_STRING, task.getPerson());
	}
	
	/*
	 * test formatting present date object as MM/DD/YYYY in a string
	 */
	@Test
	public void testTaskFormatDate1() {
		
	    Task task = new Task();
		Calendar testDeadline = new GregorianCalendar(2200, 02, 30);
	    
		Object params[] = new Object[1];
		params[0] = testDeadline.getTime();
		
	    String s = (String) PrivateAccessor.getPrivateMethod(task, "formatDate", params);
	    assertEquals (s, "03/30/2200");
	}
	
	/*
	 * test formatting past date object as MM/DD/YYYY in a string
	 */
	@Test
	public void testTaskFormatDate2() {
		Task task = new Task();
		Calendar testDeadline = new GregorianCalendar(2000, 0, 1);
		
		Object params[] = new Object[1];
		params[0] = testDeadline.getTime();
		
	    String s = (String) PrivateAccessor.getPrivateMethod(task, "formatDate", params);
	    assertEquals (s, "01/01/2000");
	}
	
	/*
	 * test formatting a null date object as MM/DD/YYYY in a string
	 * expect the string to be empty 
	 */
	@Test
	public void testTaskFormatDate3() {
		Task task = new Task();
		
		Object params[] = new Object[1];
		params[0] = null;
		
	    String s = (String) PrivateAccessor.getPrivateMethod(task, "formatDate", params);
	    assertTrue(s.equals(""));
	}
}
